# coding: utf-8
# Author: Legend奇
# @Time： 2020/7/21 下午3:39
import json
from gettext import gettext as _
from typing import Dict

from tortoise import fields

from base.base_model import MyAbstractBaseModel
from base.choices import FORM_TYPE, FORM_DATA_TYPE, YES_OR_NO, PROVIDER, DEVICE_STATUS, SERVICE_STATUS, DEPLOYMENT_TYPE, \
    SERVICE_EXCEPTION_HANDLING, PRIORITY_IP_TYPE, DEVICE_TYPE
from base.constant import Provider, ServiceStatus, ServiceDeploymentConfig
from utils.time import get_current_time_by_microsecond


class SystemConfig(MyAbstractBaseModel):
    provider = fields.IntField(default=Provider.CHINA_MOBILE, choices=PROVIDER)
    province = fields.ForeignKeyField('models.Area', null=True, on_delete=fields.SET_NULL)
    initialized = fields.BooleanField(default=True)
    domain = fields.CharField(default='ngs.localdomain', max_length=128)

    class Meta:
        table = 'system_config'
        verbose_name = _('system_config')


class Area(MyAbstractBaseModel):
    """
    level: 行政区等级 country,province,city,district
    center: 地区中心坐标 经纬度
    """
    name = fields.CharField(max_length=128, default='', verbose_name=_('area name'))
    level = fields.CharField(max_length=255, default='', verbose_name=_('area level'))
    center = fields.CharField(max_length=255, default='', verbose_name=_('area center'))
    father = fields.ForeignKeyField('models.Area', null=True, on_delete=fields.CASCADE, verbose_name=_('super area'))

    class Meta:
        table = 'area'
        verbose_name = _('area')

    def get_full_position(self):
        res = [self.name]

        def f(area):
            super_area = area.father
            if super_area:
                res.insert(0, super_area.name)
                f(super_area)

        f(self)
        return res[1:]


class EngineRoom(MyAbstractBaseModel):
    """
    full_position: 创建时根据地区来拼接的完整位置 如： 四川省/眉山市
    """
    name = fields.CharField(max_length=128, default='', verbose_name=_('engine room name'), unique=True,
                            error_messages={'unique': _('engine room already exists')})
    full_position = fields.CharField(max_length=255, default='', verbose_name=_('full position'))
    area = fields.ForeignKeyField('models.Area', null=True, on_delete=fields.SET_NULL, verbose_name=_('area id'))
    default = fields.IntField(default=0)

    class Meta:
        table = 'engineroom'
        verbose_name = _('engine room')


class Device(MyAbstractBaseModel):
    name = fields.CharField(max_length=128, default='', unique=True, verbose_name=_('device name'),
                            error_messages={'unique': _('device already exists')})
    priority_ip_type = fields.BooleanField(default=0, choices=PRIORITY_IP_TYPE, verbose_name=_('priority ip type'))
    ipv4 = fields.CharField(max_length=15, null=True, unique=True, default='',
                            verbose_name=_('device ipv4 address'))
    ipv6 = fields.CharField(max_length=25, null=True, unique=True, default='',
                            verbose_name=_('device ipv6 address'))
    os_info = fields.CharField(max_length=10, default='', null=True, verbose_name=_('device os info'))
    status = fields.SmallIntField(default=0, choices=DEVICE_STATUS, verbose_name=_('device status'))
    engine_room = fields.ForeignKeyField('models.EngineRoom', on_delete=fields.SET_NULL, null=True,
                                         verbose_name=_('engine room id'))
    device_type = fields.IntField(default=100, choices=DEVICE_TYPE, verbose_name=_('device type'))
    device_number = fields.CharField(max_length=128, null=True, unique=True, verbose_name=_('device number'))
    process_ability = fields.IntField(default=0, verbose_name=_('process ability'))
    server_name = fields.CharField(default='', max_length=128, verbose_name=_('server name'))
    position = fields.CharField(default='', max_length=256)
    gateway = fields.ForeignKeyField('models.Gateway', on_delete=fields.SET_NULL, null=True)

    class Meta:
        table = "device"
        verbose_name = _('device')


class Gateway(MyAbstractBaseModel):
    name = fields.CharField(max_length=128, default='', verbose_name=_('gateway name'))
    ipv4 = fields.CharField(max_length=128, default='', verbose_name=_('gateway ipv4'), unique=True)
    ipv6 = fields.CharField(null=True, unique=True, verbose_name=_('gateway ipv6'), default='', max_length=128)
    netmask = fields.CharField(default='', max_length=64, verbose_name=_('netmask'))
    engine_room = fields.ForeignKeyField('models.EngineRoom', on_delete=fields.SET_NULL, null=True)
    position = fields.CharField(max_length=256, default='', verbose_name=_('gateway position'))

    class Meta:
        table = 'gateway'
        verbose_name = _('gateway')


class DeviceGroup(MyAbstractBaseModel):
    name = fields.CharField(max_length=20, default='', unique=True, null=False, verbose_name=_('device group name'))
    description = fields.CharField(max_length=100, null=True, verbose_name=_('device group description'))
    devices = fields.ManyToManyField('models.Device', db_table='devicegroup_devices', on_delete=fields.CASCADE,
                                     related_name='device_group')
    default = fields.IntField(default=0)

    class Meta:
        table = 'device_group'
        verbose_name = _('device group')


class App(MyAbstractBaseModel):
    name = fields.CharField(max_length=128, default='', verbose_name=_('app name'), unique=True,
                            error_messages={'unique': _('app already exists')})
    running_version = fields.ForeignKeyField('models.AppVersion', on_delete=fields.SET_NULL, null=True,
                                             related_name='running_version', verbose_name=_('app running version id'))
    file_name = fields.CharField(max_length=64, default='', verbose_name=_('app file name'))
    package_name = fields.CharField(max_length=128, default='', verbose_name=_('app rpm package name'))
    config_path = fields.TextField(max_length=1024, null=True, default='', verbose_name=_('app config name'))
    port = fields.TextField(default=None, null=True, verbose_name=_('app port'))
    pid_file = fields.TextField(default=None, null=True, verbose_name=_('pid file'))
    install_path = fields.TextField(default=None, null=True, verbose_name=_('install path'))
    log_path = fields.TextField(default=None, null=True, verbose_name=_('install path'))
    deployment_type = fields.SmallIntField(default=0, choices=DEPLOYMENT_TYPE, verbose_name=_('deployment type'))
    must_config_data_guard = fields.SmallIntField(default=1, choices=YES_OR_NO,
                                                  verbose_name=_('must config data guard'))
    start_with_all_service = fields.SmallIntField(default=1, choices=YES_OR_NO,
                                                  verbose_name=_('start with all service'))
    service_exception_handling = fields.SmallIntField(default=0, choices=SERVICE_EXCEPTION_HANDLING,
                                                      verbose_name=_('service exception handling way'))
    app_type = fields.ForeignKeyField('models.AppType', null=True, on_delete=fields.SET_NULL)
    tags = fields.CharField(default='', max_length=255)
    daemon = fields.SmallIntField(default=0, choices=YES_OR_NO, verbose_name=_('daemon'))

    class Meta:
        table = 'app'
        verbose_name = 'app'

    @property
    def port_config(self) -> Dict:
        if not self.port:
            port_config = {}
        else:
            port_config = json.loads(self.port)
        return port_config


class AppVersion(MyAbstractBaseModel):
    version_number = fields.CharField(max_length=20, default='', verbose_name=_('version number'))
    release_note = fields.TextField(default='', verbose_name=_('release note'))
    app_type = fields.ForeignKeyField('models.AppType', null=True, on_delete=fields.SET_NULL)
    create_timestamp = fields.BigIntField(default=get_current_time_by_microsecond, verbose_name=_('create time'))
    app = fields.ManyToManyField('models.App')
    code_version = fields.IntField(default=0, null=False)

    class Meta:
        table = "appversion"
        verbose_name = _('app version')


class VersionPackageInfo(MyAbstractBaseModel):
    package_name = fields.CharField(max_length=255, unique=True, default='', verbose_name=_('package name'))
    support = fields.CharField(max_length=255, default='', verbose_name=_('os support'))
    md5 = fields.CharField(default='', unique=True, max_length=32, verbose_name=_('md5'))
    create_timestamp = fields.BigIntField(default=get_current_time_by_microsecond, verbose_name=_('create time'))
    version = fields.ForeignKeyField('models.AppVersion', on_delete=fields.SET_NULL, null=True,
                                     related_name='app_version',
                                     verbose_name=_('app version id'))

    class Meta:
        table = "versionpackageinfo"
        verbose_name = _('version package info')


class Service(MyAbstractBaseModel):
    """
    deployment_config: 应用部署方式为负荷分担方式时，所有服务为主用；而主备方式是，只有一个主用和一个备用，0：主用；1：备用
    status: 0:ready; 1:standby; 2:master;
    """
    name = fields.CharField(max_length=128, default='', verbose_name=_('service name'), unique=True,
                            error_messages={'unique': _('service already exists')})
    app = fields.ForeignKeyField('models.App', on_delete=fields.SET_NULL, null=True, verbose_name=_('app id'))
    device = fields.ForeignKeyField('models.Device', on_delete=fields.SET_NULL, null=True, verbose_name=_('device id'))
    port = fields.TextField(default=None, null=True, verbose_name=_('service port'))
    deployment_config = fields.SmallIntField(default=ServiceDeploymentConfig.MASTER, choices=DEPLOYMENT_TYPE,
                                             verbose_name=_('deployment type'))
    status = fields.SmallIntField(default=ServiceStatus.STOPPED, choices=SERVICE_STATUS,
                                  verbose_name=_('service status'))
    app_package_name = fields.CharField(default='', max_length=128, null=False)

    class Meta:
        table = 'service'
        verbose_name = _('service')

    @property
    def port_config(self) -> Dict:
        if not self.port:
            port_config = {}
        else:
            port_config = json.loads(self.port)
        return port_config


class AppType(MyAbstractBaseModel):
    name = fields.CharField(default='', max_length=128)
    package_name = fields.TextField(default='')

    class Meta:
        table = 'apptype'


class ServiceConfig(MyAbstractBaseModel):
    service_id = fields.IntField(default=0, verbose_name=_('service id'))
    file_path = fields.CharField(default='', max_length=100, null=False)
    pre_config = fields.TextField(default='', verbose_name=_('pre config content'))
    now_config = fields.TextField(default='', verbose_name=_('now config content'))
    user = fields.IntField(default=0, verbose_name=_('user id'))
    change_timestamp = fields.BigIntField(default=0, verbose_name=_('change config timestamp'))

    class Meta:
        table = 'serviceconfig'


class AppConfig(MyAbstractBaseModel):
    description = fields.CharField(default='', max_length=255, null=True)
    file_path = fields.TextField(default='', verbose_name=_('app config name'), null=False)
    app = fields.ManyToManyField('models.App', db_table='appconfig_app')

    class Meta:
        table = 'appconfig'


class ConfigTemplate(MyAbstractBaseModel):
    form_type = fields.IntField(default=1, choices=FORM_TYPE, null=True)
    data_type = fields.IntField(default=4, choices=FORM_DATA_TYPE, null=True)
    regular = fields.TextField(default='', null=True)
    options = fields.ManyToManyField('models.SelectOption', db_table='configtemplate_selectoption')
    description = fields.CharField(default='', max_length=125)
    file_id = fields.IntField(default=0, null=False)
    field_name = fields.CharField(default='', null=False, max_length=100)
    father = fields.ForeignKeyField("models.ConfigTemplate", on_delete=fields.CASCADE, null=True,
                                    related_name='father_set')
    disable = fields.IntField(default=0, choices=YES_OR_NO)

    class Meta:
        table = 'configtemplate'


class CommonConfigTemplate(MyAbstractBaseModel):
    template = fields.TextField(default='')
    app_config_id = fields.IntField(default=0, null=False)
    timestamp = fields.BigIntField(default=0, verbose_name=_('timestamp'))

    class Meta:
        table = 'commonconfigtemplate'


class SelectOption(MyAbstractBaseModel):
    name = fields.CharField(default='', max_length=100)
    data_type = fields.IntField(default=4, choices=FORM_DATA_TYPE, null=False)
    value = fields.CharField(default='', max_length=255)

    class Meta:
        table = 'selectoption'
